﻿using System;
using System.Windows.Forms;
using AuthoritExtensibility;
using Api = Authorit.API;

namespace DitaImport
{
	/// <summary>
	/// The connection to the AuthorIT5.exe user interface through the AuthoritExtensibility.dll
	/// Creates buttons in the Author-it windows client user interface
	/// Passes information to the DITA importer when the author selects the buttons
	/// </summary>
	/// <remarks></remarks>
	public class PluginManager : IPlugin
	{
		#region Fields (3) 

		private Importer _import;
		// The PluginManager exposes three functions to Author-it through the AitApplicationProxy
		// --------------------------------------------------------------------------------------
		// 1. OnImportClick()      -- display prompt for files to import --  imports into the current folder
		// 2. OnReImportClick()    -- attempts to re-import the selected objects based on a value in <IMPORT_FULL_PATH>
		// 3. OnViewSourceClick()  -- attempts to display the source file for a single object based on the value in <IMPORT_FULL_PATH>
		// 4. OnImporttDllClick()  -- displays prompt for files to reflect over -- imports contenet into current folder
		// The PluginManager also raises AitApplicationProxy.RefreshObject events back to the Author-it user interface for:
		// ----------------------------------------------------------------------------------------------------------------
		// 1. Object Modified events
		// 2. Object Created events
		/// <summary>
		/// Instance of the logging UI for the plugin.
		/// </summary>
		public frmLog _logForm = new frmLog();
		private AitApplicationProxy _proxy;
		private Config _config;

		#endregion Fields 

		#region Constructors (1) 

		/// <summary>
		/// Triggered by the Author-it Windows client so that:
		/// 1. it knows where to insert the plug-in into the Author-it Windows client user interface
		/// 2. it knows the address of the functions to call when buttons are clicked
		/// </summary>
		/// <param name="Proxy"></param>
		/// <remarks></remarks>
		public PluginManager(AitApplicationProxy Proxy)
		{
			try
			{
				_proxy = Proxy;

				_config = new Config();

				// Create a button in the Web section of the RibbonBar on the main form for importing .xml files
				ToolbarAddArgs importXmlArgs = new ToolbarAddArgs();
				importXmlArgs.PluginName = "DITA Import";  //TODO: Move toolbar PluginName to configuration settings?
				importXmlArgs.TargetForm = TargetForm.MainForm;
				importXmlArgs.TargetGroupName = "Web";  //TODO: Move TargetGroupName to configuration settings
				importXmlArgs.TargetButtonName = "DITA Import";  //TODO: Move TargetButtonName to resources
				importXmlArgs.IconImage = Properties.Resources.xml;
				importXmlArgs.Tooltip = "Converts DITA to Author-it Topics";  //TODO: Move Tooltip to resources

				// pass the address of the function to be called by the user when they click the button
				importXmlArgs.OnClick = OnImportXmlClick;
				_proxy.AddPluginToToolbar(importXmlArgs);

				// TODO: decide if the DITA import wants to be able re-import from the old source file
				//' Create a context menu that allows reimporting of in the Web section of the RibbonBar on the main form for importing .xml files
				ContextMenuAddArgs reimportXmlArgs = new ContextMenuAddArgs();
				reimportXmlArgs.PluginName = "Re-import from source file";  //TODO: Move context menu PluginName to configuration settings?
				reimportXmlArgs.SelectedObjectsMin = 1;
				reimportXmlArgs.SelectedObjectsTypes = new AuthoritExtensibility.ObjectType[] { ObjectType.Topic, ObjectType.Book };  //TODO: Consider moving SelectedObjectsTypes to configuration settings
				reimportXmlArgs.IconImage = Properties.Resources.xml;
				// pass the address of the function to be called by the user when they click the button
				reimportXmlArgs.OnClick = OnReImportClick;
				_proxy.AddPluginToObjectContextMenu(reimportXmlArgs);

				// TODO: decide if the DITA import wants to be able view the old source file
				// Create a context menu that allows reimporting of in the Web section of the RibbonBar on the main form for importing .xml files
				ContextMenuAddArgs viewSourceXmlArgs = new ContextMenuAddArgs();
				viewSourceXmlArgs.PluginName = "View source file";  //TODO: Move view source context menu PluginName to configuration settings?
				viewSourceXmlArgs.SelectedObjectsMin = 1;
				viewSourceXmlArgs.SelectedObjectsMax = 1;
				viewSourceXmlArgs.SelectedObjectsTypes = new AuthoritExtensibility.ObjectType[] { ObjectType.Topic, ObjectType.Book };  //TODO: Consider moving SelectedObjectsTypes to configuration settings
				viewSourceXmlArgs.IconImage = Properties.Resources.xml;

				// pass the address of the function to be called by the user when they click the button
				viewSourceXmlArgs.OnClick = OnViewSourceClick;
				_proxy.AddPluginToObjectContextMenu(viewSourceXmlArgs);
			}
			catch (Exception ex)
			{
				MessageBox.Show(ex.Message, "Exception in DITA Import Plugin Manager");  //TODO: Move plugin manager exception message to resources
			}
		}

		#endregion Constructors 

		#region Methods (9) 

		// Public Methods (3) 

		/// <summary>
		/// Opens a dialog that allows the user to select a file to import.
		/// </summary>
		/// <param name="sender">Object that initiated the event</param>
		/// <param name="e">Arguments for the event</param>
		public void OnImportXmlClick(object sender, AuthoritEventArgs e)
		{
			// display a log of what is happening (you could just write it out to a log file if you wanted).
			_logForm = new frmLog();
			_logForm.Clear();
			_logForm.Show();

			try
			{
				if (Connect(e))
				{
					// Let the user select the files they wish to import
					string[] files = SelectFilesToImport("DITA files (*.xml,*.dita)|*.xml;*.dita" + "|All files (*.*)|*.*", true);  //TODO: Move file extensions to configuration settings

					// Keep track of progress
					int processed = 0;
					int fileCount = files.Length;

					if (fileCount > 0)
					{
						// Set the import folder for NEW objects to the current folder
						_import.ImportFolderId = e.CurrentFolderId;
						_logForm.Log(new LogEventArgs(Importer.Severity.Information, "Import started"));
						foreach (string fileName in files)
						{
							_import.ImportFile(fileName);

							// update the user interface
							processed += 1;
							_logForm.UpdatePercentComplete(processed, fileCount, "Import files");

							// free up some memory and allow windows ui to update
							GC.Collect();
							GC.WaitForPendingFinalizers();
							Application.DoEvents();

							// see if the user has hit 'cancel' on the import progress form
							if (_logForm.CancelImport)
							{
								_logForm.Log(new LogEventArgs(Importer.Severity.Warning, "Import cancelled"));
								break;
							}
						}
						_logForm.Log(new LogEventArgs(Importer.Severity.Information, "Import finished"));
					}

					Disconnect();
				}
			}
			catch (Exception ex)
			{
				MessageBox.Show("Plugin error: " + ex.Message, "Plugin Failed", MessageBoxButtons.OK, MessageBoxIcon.Warning);
			}
		}

		/// <summary>
		/// Reprocesses the import file for the selected object.
		/// </summary>
		/// <param name="sender">Object that initiated the event</param>
		/// <param name="e">Arguments for the event</param>
		public void OnReImportClick(object sender, AuthoritEventArgs e)
		{
			// display a log of what is happening (you could just write it out to a log file if you wanted).
			_logForm = new frmLog();
			_logForm.Clear();
			_logForm.Show();
			if (MessageBox.Show("Re-import the selected objects?", "Re-import", MessageBoxButtons.YesNo) == DialogResult.Yes)
			{
				if (Connect(e))
				{
					// Keep track of progress
					int processed = 0;
					int fileCount = e.SelectedObjectIDs.Count;
					_logForm.Log(new LogEventArgs(Importer.Severity.Information, "Re-import started"));

					// Set the import folder for NEW objects to the current folder
					_import.ImportFolderId = e.CurrentFolderId;
					foreach (int objectId in e.SelectedObjectIDs)
					{
						_import.ReImportObject(objectId);

						// update the user interface
						processed += 1;
						_logForm.UpdatePercentComplete(processed, fileCount, "Re-import files");

						// free up some memory and allow the windows ui to update
						Application.DoEvents();
						GC.Collect();
						GC.WaitForPendingFinalizers();

						// see if the user has hit 'cancel' on the import progress form
						if (_logForm.CancelImport)
						{
							break;
						}
					}

					// Finish logging
					_logForm.Log(new LogEventArgs(Importer.Severity.Information, "Re-import finished"));

					Disconnect();
				}
			}
		}

		/// <summary>
		/// Opens the file from which the selected object was imported.
		/// </summary>
		/// <param name="sender">Object that initiated the event</param>
		/// <param name="e">Arguments for the event</param>
		public void OnViewSourceClick(object sender, AuthoritEventArgs e)
		{
			if (Connect(e))
			{
				int objectId = e.SelectedObjectIDs[0];
				string sourceFilePath = _import.GetSourceFile(objectId);

				if (sourceFilePath.Length == 0)
				{
					MessageBox.Show("This object doesn't have a <IMPORT_FULL_PATH> variable assignment", "View Source File", MessageBoxButtons.OK);
				}
				else if (System.IO.File.Exists(sourceFilePath))
				{
					System.Diagnostics.Process.Start(sourceFilePath);
				}
				else
				{
					MessageBox.Show(string.Format("File '{0}' not found.", sourceFilePath), "View Source File", MessageBoxButtons.OK);
				}

				Disconnect();
			}
		}
		// Private Methods (6) 

		/// <summary>
		/// Creates a connection to the Author-it library using the username and password of the connected user
		/// </summary>
		/// <param name="e"></param>
		/// <returns>True if the library connection was successful</returns>
		/// <remarks></remarks>
		private bool Connect(AuthoritEventArgs e)
		{
			Api.Configuration.LibraryConfigurationList libraryConfigList = null;
			Api.ApplicationContext applicationContext = null;

			try
			{
				// connect to the library through the Authorit.API.dll
				const string libraryAlias = "MainLibrary";  //TODO: Replace this by passing the Importer constructor the arguments
				libraryConfigList = new Api.Configuration.LibraryConfigurationList(CreateLibraryConfig(libraryAlias, e));
				applicationContext = new Api.ApplicationContext(libraryConfigList);

				// Create an Xml Importer and give it a library connection
				_import = new Importer(applicationContext, libraryAlias, e.Username, e.Password, this._config);

				// Display the results of the .Log() event in a special log UI owned by the plugin
				_import.Log += _logForm.Log;
				_import.ObjectCreated += this.ObjectCreated;
				_import.ObjectModified += this.ObjectModified;

				return true;
			}
			catch (Exception ex)
			{
				MessageBox.Show("Plugin connect error: " + ex.Message, "Plugin Failed", MessageBoxButtons.OK, MessageBoxIcon.Warning);
				return false;
			}
		}

		/// <summary>
		/// Creates an appropriate LibraryConfig based on the parameters passed from the Author-it user interface
		/// based on the current library
		/// </summary>
		/// <param name="libraryAlias"></param>
		/// <param name="e"></param>
		/// <returns></returns>
		/// <remarks></remarks>
		//TODO: Remove CreateLibraryConfig(string libraryAlias, AuthoritEventArgs e)
		private Api.Configuration.LibraryConfigurationBase CreateLibraryConfig(string libraryAlias, AuthoritEventArgs e)
		{
			Api.Configuration.LibraryConfigurationBase libraryConfig = null;

			if (e.LibraryPath != null)
			{
				// Jet
				libraryConfig = new Api.Configuration.JetLibraryConfiguration(libraryAlias, e.LibraryPath);
			}
			else
			{
				// SQLServer
				if (string.IsNullOrEmpty(e.SqlUsername))
				{
					// Trusted
					libraryConfig = new Api.Configuration.SqlServerLibraryConfiguration(libraryAlias, e.SqlServername, e.SqlDatabase);
				}
				else
				{
					libraryConfig = new Api.Configuration.SqlServerLibraryConfiguration(libraryAlias, e.SqlServername, e.SqlDatabase, e.SqlUsername, e.SqlPassword);
				}
			}

			return libraryConfig;
		}

		private void Disconnect()
		{
			_import.LogOut();

			_import.Log -= _logForm.Log;
			_import.ObjectCreated -= this.ObjectCreated;
			_import.ObjectModified -= this.ObjectModified;

			_import = null;
		}

		/// <summary>
		/// Passes ObjectCreated events from the MsImport class to with Author-it Windows user interface so the ui can be refreshed
		/// </summary>
		/// <param name="ObjectID"></param>
		/// <remarks></remarks>
		//TODO: Move ObjectCreated(int ObjectID) to Importer
		private void ObjectCreated(int ObjectID)
		{
			ObjectRefreshArgs args = new ObjectRefreshArgs();
			args.ObjectID = ObjectID;
			args.Reason = RefreshType.ObjectAdded;
			_proxy.RefreshObject(args);
		}

		/// <summary>
		/// Passes ObjectModified events from the MsImport class to with Author-it Windows user interface so the ui can be refreshed
		/// </summary>
		/// <param name="ObjectID"></param>
		/// <remarks></remarks>
		//TODO: Move ObjectModified(int ObjectID) to Importer
		private void ObjectModified(int ObjectID)
		{
			ObjectRefreshArgs args = new ObjectRefreshArgs();
			args.ObjectID = ObjectID;
			args.Reason = RefreshType.ObjectUpdated;
			_proxy.RefreshObject(args);
		}

		/// <summary>
		/// Displays an OpenFileDialog with appropriate filter so the author can select some files to import
		/// </summary>
		/// <param name="Filter">File extensions filter</param>
		/// <param name="MultiSelect">true if the user can select multiple files; otherwise, false</param>
		/// <returns>A string array containing the full paths of the selected files</returns>
		/// <remarks></remarks>
		private string[] SelectFilesToImport(string Filter, bool MultiSelect)
		{
			try
			{
				OpenFileDialog selectFiles = new OpenFileDialog();
				selectFiles.Title = "Select the files to import";  //TODO: Move select files dialog title to resources
				selectFiles.Filter = Filter;
				selectFiles.Multiselect = MultiSelect;
				selectFiles.ShowDialog();
				return selectFiles.FileNames;
			}
			catch (Exception ex)
			{
				_logForm.Log(new LogEventArgs(Importer.Severity.Error, "Select files to import exception: " + ex.Message, ex));  //TODO: //TODO: Move select files exception message to resources
				return null;
			}
		}

		#endregion Methods 
	}
}